package hudson.plugins.proc; import com.sun.tools.attach.VirtualMachine; import hudson.model.AbstractBuild; import hudson.remoting.Callable; import hudson.util.ProcessTree.OSProcess; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import java.io.File; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Set; /** * Gives Java process info for e.g stack, system properties * * @author Jitendra Kotamraju */ public class JavaProcInfo extends ProcInfo { JavaProcInfo(AbstractBuild run, OSProcess proc) { super(run, proc); } // returns the system properties for the process public Properties getSystemProperties() throws Exception { return run.getBuiltOn().getChannel().call(new PropertiesTask(""+proc.getPid())); } // returns the java stack for the process public List<String> jstack() throws Exception { return run.getBuiltOn().getChannel().call(new JstackTask(""+proc.getPid())); } // Keep it static inner class, otherwise JavaProcInfo needs to be // specified as Serializable private static class PropertiesTask extends RemoteTask<Properties> { PropertiesTask(String pid) { super(pid); } public Properties call() throws Exception { super.call(); VirtualMachine vm = null; try { vm = VirtualMachine.attach(pid); return vm.getSystemProperties(); } finally { if (vm != null) { try { vm.detach(); } catch (Exception e) { // ignore } } } } } // Keep it static inner class, otherwise JavaProcInfo needs to be // specified as Serializable private static class JstackTask extends RemoteTask<List<String>> { JstackTask(String pid) { super(pid); } public List<String> call() throws Exception { super.call(); List<String> tiList = new ArrayList<String>(); VirtualMachine vm = null; try { vm = VirtualMachine.attach(pid); String connectorAddr = vm.getAgentProperties().getProperty( "com.sun.management.jmxremote.localConnectorAddress"); if (connectorAddr == null) { String agent = vm.getSystemProperties().getProperty( "java.home") + File.separator + "lib" + File.separator + "management-agent.jar"; vm.loadAgent(agent); connectorAddr = vm.getAgentProperties().getProperty( "com.sun.management.jmxremote.localConnectorAddress"); } JMXServiceURL serviceURL = new JMXServiceURL(connectorAddr); JMXConnector connector = JMXConnectorFactory.connect(serviceURL); MBeanServerConnection mbsc = connector.getMBeanServerConnection(); ObjectName objName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME); Set<ObjectName> mbeans = mbsc.queryNames(objName, null); for (ObjectName name : mbeans) { ThreadMXBean threadBean = ManagementFactory.newPlatformMXBeanProxy( mbsc, name.toString(), ThreadMXBean.class); long threadIds[] = threadBean.getAllThreadIds(); for (long threadId : threadIds) { tiList.add(threadBean.getThreadInfo(threadId, Integer.MAX_VALUE).toString()); } } } finally { if (vm != null) { try { vm.detach(); } catch (Exception e) { // ignore } } } return tiList; } } private static class RemoteTask<T> implements Callable<T, Exception> { static final Exception TOOLS_EXCEPTION; static { Exception te = null; try { addToolsJar(); } catch(Exception e) { te = e; } TOOLS_EXCEPTION = te; } protected final String pid; protected RemoteTask(String pid) { this.pid = pid; } public T call() throws Exception { if (TOOLS_EXCEPTION != null) { throw TOOLS_EXCEPTION; } return null; } // If the slave's JVM doesn't have tools.jar in the classpath, it is added // to the system class loader. // // URLClassLoader#addURL(URL) is called reflectively with tools.jar's URL // private static void addToolsJar() throws Exception { ClassLoader cl = hudson.remoting.Channel.class.getClassLoader(); try { cl.loadClass("com.sun.tools.attach.VirtualMachine"); } catch(ClassNotFoundException ce) { if (cl instanceof URLClassLoader) { // Try to find tools.jar File jreHome = new File(System.getProperty("java.home")); File toolsJar = new File( jreHome.getParent(), "lib/tools.jar" ); if (!toolsJar.exists()) { throw new RuntimeException("Cannot find tools.jar for this slave's JVM"); } URL toolsURL = toolsJar.toURL(); Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); m.setAccessible(true); m.invoke(cl, toolsURL); } else { throw new RuntimeException("Cannot add tools.jar to Slave's system classloader"); } } } } }